﻿using Framework;
using Framework.Models;
using HtmlAgilityPack;
using Koala.JiraTool.Core.Unitys;
using ScrapySharp.Extensions;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;

namespace Koala.JiraTool.Core
{
    internal class TaskController : ITaskController
    {
        private readonly IHttpClient _httpClient;
        private readonly GlobalSettings _globalSettings;
        private readonly ITokenController _tokenController;

        public TaskController(IHttpClient httpClient, GlobalSettings globalSettings, ITokenController tokenController)
        {
            _httpClient = httpClient;
            _globalSettings = globalSettings;
            _tokenController = tokenController;
        }

        #region Implementation of ITaskController

        /// <summary>
        /// 根据任务类型获取任务集合。
        /// </summary>
        /// <param name="taskTypes">任务类型。</param>
        /// <returns>任务集合。</returns>
        public IList<TaskInfo> GetTaskList(params string[] taskTypes)
        {
            var url = GetSearchUrl(null, taskTypes, null, null);
            return GetTaskList(url);
        }

        /// <summary>
        /// 根据任务状态获取任务集合。
        /// </summary>
        /// <param name="taskStatus">任务状态。</param>
        /// <returns>任务集合。</returns>
        public IList<TaskInfo> GetTaskListByStatus(params string[] taskStatus)
        {
            var url = GetSearchUrl(null, null, null, taskStatus);
            return GetTaskList(url);
        }

        /// <summary>
        /// 根据项目名称和任务类型获取任务集合。
        /// </summary>
        /// <param name="projectNames">项目名称。</param>
        /// <param name="taskTypes">任务类型。</param>
        /// <returns>任务集合。</returns>
        public IList<TaskInfo> GetTaskListByProjectName(string[] projectNames, params string[] taskTypes)
        {
            var url = GetSearchUrl(null, taskTypes, projectNames, null);
            return GetTaskList(url);
        }

        /// <summary>
        /// 解决问题。
        /// </summary>
        /// <param name="resolveTaskInfo">解决问题信息。</param>
        public void ResolveTask(ResolveTaskInfo resolveTaskInfo)
        {
            if (resolveTaskInfo == null)
                throw new ArgumentNullException("resolveTaskInfo");
            var token = _tokenController.GetToken();
            var url = string.Format(_globalSettings.ResolveTaskUrlFormat, token);
            _httpClient.PostData(url, new NameValueCollection
            {
                {"action", "5"},
                {"id",resolveTaskInfo.TaskId},
                {"resolution", resolveTaskInfo.ResolveResult},
                {"worklog_activate", "true"},
                {"worklog_timeLogged", resolveTaskInfo.ConsumeTime},
                {"worklog_startDate", resolveTaskInfo.StartTime},
                {"worklog_adjustEstimate", "auto"},
                {"environment", resolveTaskInfo.Environment},
                {"comment", resolveTaskInfo.Remark},
                {"atl_token", token},
            });
        }

        /// <summary>
        /// 根据关键字获取任务信息。
        /// </summary>
        /// <param name="keyword">关键字。</param>
        /// <returns>任务信息。</returns>
        public TaskInfo GetTask(string keyword)
        {
            var url = string.Format(_globalSettings.TaskViewUrlFormat, keyword);

            var document = _httpClient.DownloadDocumentNode(url);
            var task = new TaskInfo
            {
                Id = GetId(document),
                Keyword = GetContent(document, "key-val"),
                Topic = GetContent(document, "summary-val"),
                User = GetUser(document, "assignee-val"),
                Speaker = GetUser(document, "reporter-val"),
                Priority = GetContent(document, "priority-val"),
                Status = GetContent(document, "status-val"),
                CreateDateTime = GetDateTime(document, "create-date"),
                UpdateDateTime = GetDateTime(document, "updated-date"),
                ExpireDateTime = GetDateTime(document, "due-date"),
                TaskType = GetContent(document, "type-val")
            };
            return task;
        }

        /// <summary>
        /// 停止任务。
        /// </summary>
        /// <param name="taskId">任务Id。</param>
        public void StopTask(string taskId)
        {
            StartOrStopTask(false, taskId);
        }

        /// <summary>
        /// 启动任务。
        /// </summary>
        /// <param name="taskId">任务Id。</param>
        public void StartTask(string taskId)
        {
            StartOrStopTask(true, taskId);
        }

        /// <summary>
        /// 停止任务。
        /// </summary>
        /// <param name="taskId">任务Id。</param>
        /// <param name="keyword">关键字。</param>
        /// <returns>是否停止成功。</returns>
        public bool StopTask(string taskId, string keyword)
        {
            return StartOrStopTask(false, taskId, keyword);
        }

        /// <summary>
        /// 启动任务。
        /// </summary>
        /// <param name="taskId">任务Id。</param>
        /// <param name="keyword">关键字。</param>
        /// <returns>是否启用成功。</returns>
        public bool StartTask(string taskId, string keyword)
        {
            return StartOrStopTask(true, taskId, keyword);
        }

        #endregion Implementation of ITaskController

        #region Private Method

        private void StartOrStopTask(bool isStart, string taskId)
        {
            var url = isStart ? _globalSettings.StartTaskUrlFormat : _globalSettings.StopTaskUrlFormat;
            var stopUrl = string.Format(url, taskId, _tokenController.GetToken());
            _httpClient.DownloadString(stopUrl);
        }

        private bool StartOrStopTask(bool isStart, string taskId, string keyword)
        {
            StartOrStopTask(isStart, taskId);
            var task = GetTask(keyword);
            if (task == null)
            {
                return false;
            }
            var status = task.Status;
            return isStart ? status == "处理中" : status == "开启";
        }

        private IList<TaskInfo> GetTaskList(string url)
        {
            var list = new List<TaskInfo>();
            var keywords = GetTaskKeyword(url);
            foreach (var keyword in keywords)
            {
                var model = GetTask(keyword);
                list.Add(model);
            }
            return list;
        }

        private string GetSearchUrl(IEnumerable<string> users, IEnumerable<string> taskTypes, IEnumerable<string> projects, IEnumerable<string> taskStatus)
        {
            var jsql = GetJSql(users, taskTypes, projects, taskStatus);
            return string.Format(_globalSettings.SearchTaskUrlFormat, jsql);
        }

        private static string GetJSql(IEnumerable<string> users, IEnumerable<string> taskTypes,
                                      IEnumerable<string> projects, IEnumerable<string> taskStatus)
        {
            var builder = new StringBuilder();
            var userJsql = GetUserJSql(users);
            var taskTypeJsql = GetTaskTypeJSql(taskTypes);
            var projectJsql = GetProjectJSql(projects);
            var statusJsql = GetTaskStatus(taskStatus);

            builder.Append(userJsql);
            if (!string.IsNullOrWhiteSpace(taskTypeJsql))
            {
                builder.Append(" and ");
                builder.Append(taskTypeJsql);
            }

            if (!string.IsNullOrWhiteSpace(projectJsql))
            {
                builder.Append(" and ");
                builder.Append(projectJsql);
            }

            if (!string.IsNullOrWhiteSpace(statusJsql))
            {
                builder.Append(" and ");
                builder.Append(statusJsql);
            }

            return builder.ToString();
        }

        private static string GetTaskStatus(IEnumerable<string> taskStatus)
        {
            return GetJSql(taskStatus, "status");
        }

        private static string GetUserJSql(IEnumerable<string> users)
        {
            var list = users == null ? new string[0] : users.ToArray();

            return !list.Any() ? "assignee in (currentUser())" : GetJSql(list, "assignee");
        }

        private static string GetTaskTypeJSql(IEnumerable<string> taskTypes)
        {
            return GetJSql(taskTypes, "issuetype");
        }

        private static string GetProjectJSql(IEnumerable<string> peojects)
        {
            return GetJSql(peojects, "project");
        }

        private static string GetJSql(IEnumerable<string> values, string fieldName)
        {
            if (values == null)
                return string.Empty;

            var builder = new StringBuilder();
            foreach (var value in values)
            {
                builder.Append(',');
                builder.Append(value);
            }
            if (builder.Length > 0)
            {
                builder.Remove(0, 1);
                builder.Insert(0, string.Format("{0} in (", fieldName));
                builder.Append(")");
            }
            return builder.ToString();
        }

        private string[] GetTaskKeyword(string url)
        {
            var keywords = new List<string>();
            var document = _httpClient.DownloadDocumentNode(url);
            foreach (var htmlNode in document.CssSelect("#issuetable tbody tr"))
            {
                var keyword = Replace(htmlNode.Attributes["data-issuekey"].Value);
                keywords.Add(keyword);
            }
            return keywords.ToArray();
        }

        /*private static string GetTaskType(HtmlNode document, out  string taskTypeString)
        {
                        taskTypeString = string.Empty;
                        var node = document.CssSelect("#type-val").FirstOrDefault();
                        if (node == null)
                        {
                            return TaskType.Unknown;
                        }
                        var str = Replace(node.InnerText);
                        taskTypeString = str;
                        switch (node.InnerText)
                        {
                            case "描述":
                                return TaskType.Description;

                            case "改进":
                                return TaskType.Improve;

                            case "新任务":
                                return TaskType.New;

                            case "新功能":
                                return TaskType.Function;

                            case "子任务":
                                return TaskType.Child;

                            default:
                                return TaskType.Unknown;
                                break;
                        }
        }*/

        private static string GetId(HtmlNode document)
        {
            var input = document.CssSelect("input[name='id']").FirstOrDefault();
            if (input == null)
                return string.Empty;

            var oValue = input.Attributes["value"];
            if (oValue == null)
                return string.Empty;

            var oAValue = oValue.Value;
            return oAValue == null ? string.Empty : Replace(oAValue);
        }

        private static string GetContent(HtmlNode document, string id)
        {
            var node = document.CssSelect("#" + id).FirstOrDefault();
            return node == null ? string.Empty : Replace(node.InnerText);
        }

        private static string GetUser(HtmlNode document, string id)
        {
            var node = document.CssSelect(string.Format("#{0} span", id)).FirstOrDefault();
            return node == null ? string.Empty : Replace(node.InnerText);
        }

        private static DateTime? GetDateTime(HtmlNode document, string id)
        {
            var node = document.CssSelect(string.Format("#{0} time", id)).FirstOrDefault();
            if (node == null)
            {
                return null;
            }
            var time = Replace(node.Attributes["datetime"].Value);
            DateTime dateTime;
            if (DateTime.TryParse(time, out dateTime))
            {
                return dateTime;
            }
            return null;
        }

        private static string Replace(string text)
        {
            return text.Replace("\r", string.Empty).Replace("\n", string.Empty).Replace("\t", string.Empty).Trim();
        }

        #endregion Private Method
    }
}